package bcontractor.base;

import java.util.Collection;
import java.util.Iterator;
import java.util.Set;

import org.apache.commons.lang.builder.HashCodeBuilder;

import bcontractor.api.Sentence;
import bcontractor.api.SentenceSet;

/**
 * Abstract class to avoid needles implementations of the SentenceSet
 * operations.
 * 
 * @author lundberg
 * 
 * @param <B>
 *            kind of sentence set.
 * @param <S>
 *            sentence it stores.
 */
public abstract class AbstractSentenceSet<B extends SentenceSet<S>, S extends Sentence<S>> implements SentenceSet<S> {

    protected final IncrementalSet<S> sentences;

    /**
     * Constructor
     */
    public AbstractSentenceSet() {
        this.sentences = new IncrementalSet<S>();
    }

    /**
     * Constructor
     * 
     * @param sentences
     *            sentences
     */
    protected AbstractSentenceSet(IncrementalSet<S> sentences) {
        this.sentences = sentences;
    }

    /**
     * Abstract method, creates a new instance of sentence set containing the
     * given incremental set.
     * 
     * @param set
     *            set
     * @return new sentence set
     */
    protected abstract B createWith(IncrementalSet<S> set);

    /**
     * Creates a new sentence set containing the current sentences plus the ones
     * given.
     * 
     * @param sentences
     *            sentences
     * @return new sentence set
     */
    public B with(Collection<? extends S> sentences) {
        return this.createWith(this.sentences.with(sentences));
    }

    public B with(SentenceSet<? extends S> sentences) {
        return this.createWith(this.sentences.with(sentences.asSet()));
    }

    public B with(S sentence) {
        return this.createWith(this.sentences.with(sentence));
    }

    public B without(S sentence) {
        return this.createWith(this.sentences.without(sentence));
    }

    public B without(Collection<? extends S> sentences) {
        return this.createWith(this.sentences.without(sentences));
    }

    public B without(SentenceSet<? extends S> sentences) {
        return this.without(sentences.asSet());
    }

    public B intersection(SentenceSet<? extends S> sentences) {
        return this.createWith(this.sentences.intersection(sentences.asSet()));
    }

    public Set<S> asSet() {
        return this.sentences.asSet();
    }

    public int size() {
        return this.sentences.size();
    }

    public boolean isEmpty() {
        return this.sentences.isEmpty();
    }

    public boolean contains(S sentence) {
        return this.sentences.contains(sentence);
    }

    public boolean containsAll(Iterable<? extends S> sentences) {
        return this.sentences.containsAll(sentences);
    }

    /**
     * {@inheritDoc}
     */
    public Iterator<S> iterator() {
        return this.sentences.build().iterator();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean equals(Object obj) {
        if (!this.getClass().isInstance(obj)) {
            return false;
        }
        AbstractSentenceSet<?, ?> o = (AbstractSentenceSet<?, ?>) obj;
        return this.sentences.equals(o.sentences);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public int hashCode() {
        return new HashCodeBuilder().append(this.sentences).toHashCode();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String toString() {
        StringBuilder builder = new StringBuilder();
        builder.append(this.getClass().getSimpleName());
        builder.append("\n\tSentences:");
        for (S s : this) {
            builder.append("\n\t\t");
            builder.append(s.toString().trim().replaceAll("\n", "\n\t\t"));
        }
        return builder.toString();
    }

}